#ifndef RTSP_SERVER_H
#define RTSP_SERVER_H

#include <iostream>
#include <list>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

using namespace std;

list<int> clients_list;
/********* macro defintion ********************/
//server ip
#define SERVER_IP "0.0.0.0"

//server port
#define SERVER_PORT 554

//epoll size
#define EPOLL_SIZE 5000

//message buffer size
#define BUF_SIZE 0xFFFF

#define BUFF_LEN 1024

#define SERVER_WELCOME "Welcome you join to the chat room! Your chat ID is: Client #%d"

#define SERVER_MESSAGE "ClientID %d say >> %s"

#define CLIENT_SESSION "kywzQ134"

//exit
#define EXIT "EXIT"

#define CAUTION "There is only one int the char room!"

int server_rtp_fd;
/******************* some function ***************************/
/***
 *  @param sockfd: socket descriptor
 *  @return 0
 * */
int setnonblocking(int sockfd)
{
    fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0) | O_NONBLOCK);
    return 0;
}

/***
 *  @param epoolfd: epool handle
 *  @param fd: socket descriptor
 *  @param enable_et: enable_et = true, epoll use ET; otherwise LT
 * */
void addfd(int epollfd, int fd, bool enable_et)
{
    struct epoll_event ev;
    ev.data.fd = fd;
    ev.events = EPOLLIN;
    if(enable_et){
        ev.events = EPOLLIN | EPOLLET;
    }

    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
    setnonblocking(fd);
    printf("fd added to epoll!\n");
}

/***
 *  @param clientfd: socket descriptor 
 *  @return: len
 * */
int sendBroadcastMessage(int clientfd)
{
    // buf[BUF_SIZE] receive new char message
    // message[BUF_SIZE] save format message
    char buf[BUF_SIZE], message[BUF_SIZE];
    bzero(buf, BUF_SIZE);
    bzero(message, BUF_SIZE);

    // receive message
    printf("read from client(clientID = %d)\n", clientfd);
    int len = recv(clientfd, buf, BUF_SIZE, 0);

    if(len == 0) // len = 0 means the client closed connection
    {
        close(clientfd);
        clients_list.remove(clientfd); // server remove the client
        printf("ClientID = %d closed.\n now there are %d client in the char room\n", clientfd, clients_list.size());
    }
    else    // broadcast message
    {
        if(clients_list.size() == 1) // this means there is only one int the char room
        {
            send(clientfd, CAUTION, strlen(CAUTION), 0);
            return len;
        }

        // format message to broadcast
        sprintf(message, SERVER_MESSAGE, clientfd, buf);
        list<int>::iterator it;
        for(it = clients_list.begin(); it != clients_list.end(); ++it)
        {
            if(*it != clientfd)
            {
                if(send(*it, message, BUF_SIZE, 0) < 0){
                    perror("error");
                    exit(-1);
                }
            }
        }
    }

    return len;
}

void handle_udp_msg(int fd)
{
    char buf[BUFF_LEN]; // 接收缓冲区，1024字节
    socklen_t len;
    int count;
    struct sockaddr_in client_addr; // client_addr用于记录发送方的地址信息
    FILE *fp = NULL;
    fp = fopen("te11.h264", "wb+");
    while(1)
    {
        memset(buf, 0, BUFF_LEN);
        len = sizeof(client_addr);
        count = recvfrom(fd, buf, BUFF_LEN, 0, (struct sockaddr*)&client_addr, &len);
        if(count == -1){
            printf("recieve data fail!\n");
            return;
        }
        fwrite(buf, sizeof(char), sizeof(buf), fp);
        std::cout << buf << std::endl;
    }

    fclose(fp);
}

void requestHandler(int clientfd)
{
    char buf[BUF_SIZE], message[BUF_SIZE];
    bzero(buf, BUF_SIZE);
    bzero(message, BUF_SIZE);

    // receive message
    printf("read from client(clientID = %d)\n", clientfd);
    int len = recv(clientfd, buf, BUF_SIZE, 0);

    if(len > 0){
        char method[64] = {0};
        char url[512] = {0};
        char version[64] = {0};

        if(sscanf(buf, "%s %s %s", method, url, version) != 3) {
            return; 
        }

        if(strcmp("OPTIONS", method) == 0){
            sprintf(message, "RTSP/1.0 200 OK\r\n"
            "CSeq: 1\r\n"
            "Session: %s\r\n"
            "Public: OPTIONS, ANNOUNCE, SETUP, RECORD, TEARDOWN\r\n\r\n",
            CLIENT_SESSION);

            send(clientfd, message, strlen(message), 0);
        }else if(strcmp("ANNOUNCE", method) == 0){
            sprintf(message, "RTSP/1.0 200 OK\r\n"
            "CSeq: 2\r\n"
            "Session: %s\r\n\r\n",
            CLIENT_SESSION);

            send(clientfd, message, strlen(message), 0);
        }else if(strcmp("SETUP", method) == 0){
            sprintf(message, "RTSP/1.0 200 OK\r\n"
            "CSeq: 3\r\n"
            "Session: %s\r\n"
            "Transport: RTP/AVP/UDP;unicast;client_port=12034-12035;server_port=34872-34873;mode=record\r\n\r\n",
            CLIENT_SESSION);

            struct sockaddr_in server_rtp_addr;

            // AF_INET: IPV4; SOCK_DGRAM:UDP
            if((server_rtp_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
                printf("create socket fail!\n");
                return;
            }
            int on = 1;
            setsockopt(server_rtp_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
            bzero(&server_rtp_addr, sizeof(server_rtp_addr));
            server_rtp_addr.sin_family = AF_INET;
            server_rtp_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
            server_rtp_addr.sin_port = htons(34872);

            if(bind(server_rtp_fd, (struct sockaddr*)&server_rtp_addr, sizeof(server_rtp_addr)) < 0){
                printf("socket bind fail!\n");
                return;
            }
            send(clientfd, message, strlen(message), 0);
        }else if(strcmp("RECORD", method) == 0){
            sprintf(message, "RTSP/1.0 200 OK\r\n"
            "CSeq: 4\r\n"
            "Session: %s\r\n\r\n",
            CLIENT_SESSION);

            send(clientfd, message, strlen(message), 0);

            pid_t fpid;
            fpid = fork();
            if(fpid < 0){
                printf("error in fork!");
            }else if(fpid == 0){
                handle_udp_msg(server_rtp_fd);
                close(server_rtp_fd);
            }
        }else if(strcmp("TEARDOWN", method) == 0){
            sprintf(message, "RTSP/1.0 200 OK\r\n"
            "CSeq: 5\r\n"
            "Session: %s\r\n\r\n",
            CLIENT_SESSION);

            send(clientfd, message, strlen(message), 0); 
        }
    }
}
#endif  // RTSP_SERVER_H